/*
 * queue manager test
 *
 * test for lib/queue.c
 *
 * $Id: test_queue.c 29 2009-05-05 16:31:09Z ybc2084 $
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <assert.h>
#include <errno.h>
#include "type.h"
#include "queue.h"

/******************************************
 * declaration
 ******************************************/
static void TestQueue(void);
static void TestQueueNode(void);


int main(void)
{
	TestQueue();

	TestQueueNode();

	printf("0K:" __FILE__ ":all test cases passed\n");

	return (0);
}

/*
 * data for testing
 */
static	int		iArr[] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9};
static	char	cArr[] = {'a', 'b', 'c', 'd', 'e', 'f'};
static	char   *sArr[] = {
						"I'm rockins", 
						"who are you", 
						"",
						"nice to meet you",
						NULL
					};

/*
 * test queue interface
 */
static void TestQueue(void)
{

	Queue  	   *theQ;
	QueueNode  *node;
	bool		isempty;
	bool	    isfull;
	int		    i;

	/***************************************************
	 * trial test NewQueue() and DelQueue():
	 * 1. create an empty queue
	 * 2. destroy it
	 ***************************************************/
	theQ = NewQueue();
	assert(theQ);
	DelQueue(theQ);

	/***************************************************
	 * test isEmpty() and isFull():
	 * 1. queue empty
	 * 2. queue contains nodes
	 ***************************************************/
	theQ = NewQueue();
	assert(theQ);

	isempty = theQ->isEmpty(theQ);
	isfull	= theQ->isFull(theQ);
	assert(isempty == true);
	assert(isfull == false);
	
	theQ->enqueue(theQ, NULL);
	isempty = theQ->isEmpty(theQ);
	isfull	= theQ->isFull(theQ);
	assert(isempty == false);
	assert(isfull == false);

	DelQueue(theQ);

	/*****************************************************
	 * test enqueue() on int pointer:
	 * 1. create a queue contains ptr to int
	 * 2. call purge() to free all entity
	 *****************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(iArr)/sizeof(iArr[0]); i++) {
		int *pi = (int *)malloc(sizeof(int));
		assert(pi);
		*pi = iArr[i];
		theQ->enqueue(theQ, pi);
	}

	theQ->purge(theQ, true);
	DelQueue(theQ);

	/*****************************************************
	 * test enqueue() on char pointer:
	 * 1. create a queue contains ptr to char 
	 * 2. call purge() to free all entity
	 *****************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(cArr)/sizeof(cArr[0]); i++) {
		char *pc = (char *)malloc(sizeof(char));
		assert(pc);
		*pc = iArr[i];
		theQ->enqueue(theQ, pc);
	}

	theQ->purge(theQ, true);
	DelQueue(theQ);

	/***************************************************
	 * test enqueue() on string pointer:
	 * 1. fill in each queue entity with string address
	 * 2. do not call q->purge(), since string is not
	 * 	  allocated from heap
	 ***************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(sArr)/sizeof(sArr[0]); i++) {
		char *ps = sArr[i];
		theQ->enqueue(theQ, ps);
	}

	DelQueue(theQ);

	/******************************************************
	 * test dequeue():
	 * dequeue an entity from queue, the dequeued entity
	 * must be NULL
	 ******************************************************/
	theQ = NewQueue();
	assert(theQ);

	void *p = theQ->dequeue(theQ);
	assert(p == NULL);

	DelQueue(theQ);

	/*********************************************************
	 * test dequeue() and compare dequeued entity's value with
	 * pushed value originally
	 *********************************************************/
	/*****************************************************
	 * test dequeued value point by int pointer:
	 * 1. create a queue contains ptr to int
	 * 2. dequeue every entity and check
	 * 3. free dequeued entity manually
	 *****************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(iArr)/sizeof(iArr[0]); i++) {
		int *pi = (int *)malloc(sizeof(int));
		assert(pi);
		*pi = iArr[i];
		theQ->enqueue(theQ, pi);
	}

	for (i = 0; i < sizeof(iArr)/sizeof(iArr[0]); i++) {
		int *pi = (int *)theQ->dequeue(theQ);
		assert(pi);
		assert(*pi == iArr[i]);
		free(pi);
	}

	/* don't call purge(), instead, use valgrind to check
	 * whether memory leakage
	 */
	DelQueue(theQ);

	/*****************************************************
	 * test dequeued value point by char pointer:
	 * 1. create a queue contains ptr to char 
	 * 2. dequeue entity and compare
	 *****************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(cArr)/sizeof(cArr[0]); i++) {
		char *pc = (char *)malloc(sizeof(char));
		assert(pc);
		*pc = cArr[i];
		theQ->enqueue(theQ, pc);
	}

	for (i = 0; i < sizeof(cArr)/sizeof(cArr[0]); i++) {
		char *pc = (char *)theQ->dequeue(theQ);
		assert(pc);
		assert(*pc == cArr[i]);
		free(pc);
	}

	/* don't call purge(), instead, use valgrind to check
	 * whether memory leakage
	 */
	DelQueue(theQ);

	/***************************************************
	 * test dequeue value point by string pointer:
	 * 1. fill in each queue entity with string address
	 * 2. dequeue and compare entity value
	 * 3. no purge(), no free(), string is statically stored
	 ***************************************************/
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(sArr)/sizeof(sArr[0]); i++) {
		char *ps = (char *)sArr[i];
		theQ->enqueue(theQ, ps);
	}

	for (i = 0; i < sizeof(sArr)/sizeof(sArr[0]); i++) {
		char *ps = (char *)theQ->dequeue(theQ);
		assert(ps == sArr[i]);
		if (ps && (ps == sArr[i])) {
			int isequal = strcmp(ps, sArr[i]);
			assert(isequal == 0);
		}
	}

	DelQueue(theQ);

	/****************************************************
	 * test purge() on empty queue
	 * 1. set release flag 'true'
	 * 2. set release flag 'false'
	 ****************************************************/
	theQ = NewQueue();
	assert(theQ);

	theQ->purge(theQ, true);
	theQ->purge(theQ, false);

	DelQueue(theQ);

	/****************************************************
	 * test getHead():
	 * in default case, getHead() should return non-NULL
	 ****************************************************/
	theQ = NewQueue();
	assert(theQ);

	assert(theQ->getHead(theQ));

	DelQueue(theQ);

	/***************************************************
	 * test setHead():
	 * 1. setHead() to NULL should not affect
	 * 2. setHead() to new node should be okay
	 ***************************************************/
	theQ = NewQueue();
	assert(theQ);

	assert(theQ->setHead(theQ, NULL) == NULL);

	node = NewQueueNode();
	assert(node);
	assert(theQ->setHead(theQ, node) == node);

	DelQueue(theQ);
}


/*
 * test queue node interface
 */
static void TestQueueNode(void)
{
	Queue		*theQ;
	QueueNode	*head;
	QueueNode	*node;
	int			 i;

	/* prepare a queue */
	theQ = NewQueue();
	assert(theQ);

	for (i = 0; i < sizeof(iArr)/sizeof(iArr[0]); i++) {
		int *pi = (int *)malloc(sizeof(int));
		assert(pi);
		*pi = iArr[i];
		theQ->enqueue(theQ, pi);
	}

	head = theQ->getHead(theQ);

	/******************************************************
	 * test lock() and unlock()
	 ******************************************************/
	for (node = head->getNextNode(head);
		 node != head; node = node->getNextNode(node)) {
		int	rep = 10;
		for (i = 0; i < rep; i++) {
			/*
			 * nomal case
			 */
			node->lock(node);
			/* do sth. */
			node->unlock(node);

			/*
			 * abnomal case, pass NULL as object
			 */
			node->lock(NULL);
			/* do sth. */
			node->unlock(NULL);
		}
	}
	
	/******************************************************
	 * test get/del/ins-PrevNode():
	 * 1. pass in valid object
	 * 2. pass in NULL as object
	 ******************************************************/
	for (node = head->getNextNode(head);
		 node != head; node = node->getNextNode(node)) {
		QueueNode	*pgNode;
		QueueNode	*pdNode;
		QueueNode	*piNode;

		/* normal case */
		pgNode = node->getPrevNode(node);
		pdNode = node->delPrevNode(node);
		piNode = node->insPrevNode(node, pdNode);

		assert(pgNode && pdNode && piNode && (pgNode == pdNode) 
				&& (pgNode == piNode) && (pdNode == piNode));

		/* abnormal case */
		pgNode = node->getPrevNode(NULL);
		pdNode = node->delPrevNode(NULL);
		piNode = node->insPrevNode(NULL, NULL);
		assert((pgNode == NULL) && (pdNode == NULL) && (piNode == NULL));
	}

	/******************************************************
	 * test get/del/ins-NextNode():
	 * 1. pass in valid object
	 * 2. pass in NULL as object
	 ******************************************************/
	for (node = head->getNextNode(head);
		 node != head; node = node->getNextNode(node)) {
		QueueNode	*ngNode;
		QueueNode	*ndNode;
		QueueNode	*niNode;

		/* normal case */
		ngNode = node->getNextNode(node);
		ndNode = node->delNextNode(node);
		niNode = node->insNextNode(node, ndNode);

		assert(ngNode && ndNode && niNode && (ngNode == ndNode) 
				&& (ngNode == niNode) && (ndNode == niNode));

		/* abnormal case */
		ngNode = node->getNextNode(NULL);
		ndNode = node->delNextNode(NULL);
		niNode = node->insNextNode(NULL, NULL);
		assert((ngNode == NULL) && (ndNode == NULL) && (niNode == NULL));
	}

	/*************************************************************
	 * test get/set-Data():
	 *************************************************************/
	for (i = 0, node = head->getNextNode(head);
		 node != head; i++, node = node->getNextNode(node)) {

		/* 
		 * normal case:
		 * get old data and set new data 
		 */ 
		int *pv;
		pv = (int *)node->getData(node);
		assert(pv && (*pv == iArr[i]));

		int *pnv;
		pnv = malloc(sizeof(int));
		assert(pnv);
		*pnv = *pv + 1;

		pv = (int *)node->setData(node, pnv);
		assert(pv && (*pv == iArr[i]) && 
				pnv && (*pnv == iArr[i] + 1));
		free(pv);

		/*
		 * abnormal case:
		 * 1. pass in NULL node
		 * 2. pass in NULL data
		 */
		assert(node->getData(NULL) == NULL);
		int *pData = malloc(sizeof(int));
		assert(pData);
		*pData = 0xdeadbeef;

		/* node = NULL, data = non-NULL */
		assert(node->setData(NULL, pData) == NULL);
		free(pData);

		/* node = non-NULL, data = NULL */
		pData = NULL;
		assert(node->setData(node, pData) == NULL);

		/* node = NULL, data = NULL */
		assert(node->setData(NULL, pData) == NULL);
	}

	/* recycle */
	theQ->purge(theQ, true);
	DelQueue(theQ);
}

